/*
 * Copyright 2010 Biz-e (info@biz-e.nl)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.biz_e.acl.server;

import java.util.ArrayList;
import java.util.List;

import nl.biz_e.acl.client.AuthorizationService;
import nl.biz_e.acl.server.dao.AuthorizationDao;
import nl.biz_e.acl.server.dao.ContextDao;
import nl.biz_e.acl.server.dao.UserDao;
import nl.biz_e.acl.server.dao.UserGroupDao;
import nl.biz_e.acl.shared.model.Authorization;
import nl.biz_e.acl.shared.model.Context;
import nl.biz_e.acl.shared.model.User;
import nl.biz_e.acl.shared.model.UserGroup;
import nl.biz_e.appengine.lib.server.objectify.ObjectifyUtils;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;

/**
 * Remote implementation of the {@link AuthorizationService} interface
 * 
 * @author Joost A. Bloemsma
 * 
 */
public class AuthorizationServiceImpl extends RemoteServiceServlet implements
        AuthorizationService {

    private static final long serialVersionUID = 1L;
    private static final AuthorizationDao authDao = new AuthorizationDao();
    private static final UserGroupDao groupDao = new UserGroupDao();
    private static final UserDao userDao = new UserDao();
    private static final ContextDao ctxDao = new ContextDao();

    /**
     * {@inheritDoc}
     */
    @Override
    public Authorization getAuthorization(User user, Context ctx) {
        return authDao.get(user, ctx);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Authorization getAuthorization(UserGroup group, Context ctx) {
        return authDao.get(group, ctx);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Authorization> getAuthorizations(User user) {
        return authDao.getAuthorizations(user);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Authorization> getAuthorizations(UserGroup group) {
        return authDao.getAuthorizations(group);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Authorization> getAuthorizations(Context ctx) {
        Objectify ofy = ObjectifyService.begin();
        Query<Authorization> q = ofy.query(Authorization.class).filter(
                "context", ctx);
        return q.list();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteAuthorization(Authorization authorization) {
        if (authorization != null)
            authDao.delete(authorization);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteAuthorization(User user, Context ctx) {
        deleteAuthorization(this.getAuthorization(user, ctx));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteAuthorization(UserGroup group, Context ctx) {
        deleteAuthorization(this.getAuthorization(group, ctx));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<UserGroup> getAuthorizedGroups(Context ctx) {
        List<Authorization> auths = getAuthorizations(ctx);
        ArrayList<UserGroup> groups = new ArrayList<UserGroup>();
        for (Authorization auth : auths) {
            Key<UserGroup> groupKey = auth.getUserGroup();
            if (groupKey != null) {
                groups.add(groupDao.get(groupKey));
            }
        }
        return groups;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<User> getAuthorizedUsers(Context ctx) {
        List<Authorization> auths = getAuthorizations(ctx);
        ArrayList<User> users = new ArrayList<User>();
        for (Authorization auth : auths) {
            Key<User> userKey = auth.getUser();
            if (userKey != null) {
                users.add(userDao.get(userKey));
            }
        }
        return users;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Boolean isAuthorized(User user, Context ctx) {
        Authorization auth = getAuthorization(user, ctx);
        if (auth != null)
            return auth.isApproved();
        UserServiceImpl userService = new UserServiceImpl();
        List<UserGroup> groups = userService.getMemberships(user);
        for (UserGroup group : groups) {
            auth = getAuthorization(group, ctx);
            if (auth != null)
                return auth.isApproved();
        }
        Context parent = ctxDao.getParentContext(ctx);
        if (parent != null) {
            return isAuthorized(user, parent);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Boolean isAuthorized(UserGroup group, Context ctx) {
        Authorization auth = getAuthorization(group, ctx);
        if (auth != null)
            return auth.isApproved();
        Context parent = ctxDao.getParentContext(ctx);
        if (parent != null) {
            return isAuthorized(group, parent);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setAuthorization(User user, Context ctx, boolean approved) {
        Authorization auth = getAuthorization(user, ctx);
        if (auth == null) {
            userDao.save(user);
            ctxDao.save(ctx);
            auth = new Authorization();
            auth.setUser(ObjectifyUtils.generateKey(user));
            auth.setContext(ObjectifyUtils.generateKey(ctx));
        }
        auth.setApproved(approved);
        authDao.save(auth);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setAuthorization(UserGroup group, Context ctx, boolean approved) {
        Authorization auth = getAuthorization(group, ctx);
        if (auth == null) {
            groupDao.save(group);
            ctxDao.save(ctx);
            auth = new Authorization();
            auth.setUserGroup(ObjectifyUtils.generateKey(group));
            auth.setContext(ObjectifyUtils.generateKey(ctx));
        }
        auth.setApproved(approved);
        authDao.save(auth);
    }
}
